package org.truenewx.tnxjeex.unit.support;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.truenewx.tnxjee.core.Strings;
import org.truenewx.tnxjee.core.caption.CaptionUtil;
import org.truenewx.tnxjee.core.enums.EnumItem;
import org.truenewx.tnxjee.core.enums.EnumType;
import org.truenewx.tnxjee.core.enums.EnumTypeParserImpl;
import org.truenewx.tnxjee.core.spec.Mutability;
import org.truenewx.tnxjee.core.util.*;
import org.truenewx.tnxjeex.unit.model.MeasureUnit;

@Component
public class MeasureUnitEnumTypeParser extends EnumTypeParserImpl {

    @Override
    protected Resource[] getResources(Locale locale) {
        return IOUtil.findI18nResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "META-INF/units",
                locale, FileExtensions.XML);
    }

    @Override
    protected List<EnumType> parse(Resource resource, Locale locale) {
        List<EnumType> list = new ArrayList<>();
        try (InputStream in = resource.getInputStream()) {
            SAXReader reader = new SAXReader();
            Document doc = reader.read(in);
            Element rootElement = doc.getRootElement();
            int nameSize = MathUtil.parseInt(rootElement.attributeValue("name-size"));
            List<Element> typeElements = rootElement.elements("unit");
            for (Element typeElement : typeElements) {
                String typeName = typeElement.attributeValue("name");
                Assert.isTrue(MeasureUnits.isUnitType(typeName), () -> "The type name '" + typeName +
                        "' is not a valid measure unit type");
                Assert.isTrue(nameSize <= 0 || typeName.length() <= nameSize,
                        () -> "The type name('" + typeName + "') length has exceeded " + nameSize);

                Class<? extends MeasureUnit<?>> unitClass = MeasureUnits.getInstance().getUnitClass(typeName);
                String typeSubname = typeElement.attributeValue("subname");
                if (StringUtils.isBlank(typeSubname)) { // 子类型名称为空，则类型名称不能为已有的固定单位
                    Assert.isNull(unitClass,
                            () -> "The subname is blank, so the name cannot be an existing measure unit: " + typeName);
                }

                String typeCaption = typeElement.attributeValue("caption");
                if (StringUtils.isBlank(typeCaption)) {
                    if (unitClass == null) {
                        typeCaption = typeName;
                        if (StringUtils.isNotBlank(typeSubname)) {
                            typeCaption += Strings.DOT + typeSubname;
                        }
                    } else {
                        typeCaption = CaptionUtil.getCaption(unitClass, LocaleContextHolder.getLocale());
                    }
                }
                EnumType enumType = new EnumType(typeName, typeSubname, typeCaption, Mutability.NONE);
                addEnumItemsToEnumType(typeElement, enumType, locale);
                list.add(enumType);
            }
        } catch (DocumentException | IOException e) {
            LogUtil.error(getClass(), e);
        }
        return list;
    }

    @Override
    protected EnumItem buildEnumItem(Element itemElement, int ordinal) {
        String key = itemElement.attributeValue("key");
        MeasureUnit<?> unit = MeasureUnits.getInstance().fromKey(key);
        Assert.notNull(unit, () -> "'" + key + "' is not a valid measure unit key");
        String caption = itemElement.attributeValue("caption");
        if (StringUtils.isBlank(caption)) {
            Class<? extends MeasureUnit<?>> unitClass = MeasureUnits.getUnitClass(unit);
            Field field = ClassUtil.getStaticPublicField(unitClass, key);
            // 单位实例都一定能找到Field，且Field上一定有@Caption
            caption = CaptionUtil.getCaption(field, LocaleContextHolder.getLocale());
        }
        EnumItem item = new EnumItem(ordinal, key, caption, Mutability.NONE);
        item.setValue(unit.getValue());
        return item;
    }
}
